#include "norflash_drv.h"

/**
 * @desc  : norflash初始化
 * @date  : 2025-04-24
 * @author: AnRui
 * @note  :
 */
void NorFlashDrvInit(void) {
    // CS 推挽输出
    rcu_periph_clock_enable(NOR_FLASH_CS_RCU);
    gpio_init(NOR_FLASH_CS_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, NOR_FLASH_CS_PIN);
    // CS拉高 不选中
    NOR_FLASH_CS(1);
    // SPI初始化
    SPI_INIT_FUNCTION();
}



/**
 * @desc  : 读取Norflash的 ID
 * @date  : 2025-04-24
 * @author: AnRui
 * @param : mid - {参数说明}: 存储制造商ID
 * @param : did - {参数说明}: 存储设备ID
 * @note  :
 */
void ReadNorflashID(uint8_t* mid, uint16_t* did) {
    NOR_FLASH_CS_SELECT();
    SPI_RW_FUNCTION(NORFLASH_JEDEC_ID);
    *mid = SPI_RW_FUNCTION(DUMMY_BYTE);
    *did = SPI_RW_FUNCTION(DUMMY_BYTE) << 8;
    *did |= SPI_RW_FUNCTION(DUMMY_BYTE);
    NOR_FLASH_CS_DESELECT();
}

/**
 * @desc  : 指定地址开始读出指定个数的数据
 * @date  : 2025-04-24
 * @author: AnRui
 * @param : readAddr - {参数说明}:  readAddr,读取地址
 * @param : pBuffer - {参数说明}:  pBuffer,数组首地址
 * @param : len - {参数说明}: len,要读出的数据个数
 * @note  : 读数据过程
 *          1.先发送读的功能码：
 *          2.发送24位地址  高位 中位 低位
 *          3.读取数据
 */
void ReadNorflashData(uint32_t readAddr, uint8_t* pBuffer, uint32_t len) {
    NOR_FLASH_CS_SELECT();
    SPI_RW_FUNCTION(NORFLASH_READ_DATA);
    SPI_RW_FUNCTION((readAddr & 0xFF0000) >> 16);
    SPI_RW_FUNCTION((readAddr & 0x00FF00) >> 8);
    SPI_RW_FUNCTION(readAddr & 0xFF);
    for (uint32_t i = 0; i < len; i++) {
        pBuffer[i] = SPI_RW_FUNCTION(DUMMY_BYTE);
    }
    NOR_FLASH_CS_DESELECT();
}

/**
 * @desc  : 开启写功能
 * @date  : 2025-04-24
 * @author: AnRui
 * @note  : 在每次页面编程（PP）、扇区擦除（SE）、块擦除（BE）、芯片擦除（CE）、写状态寄存器（WRSR）
 *          和擦除/编程安全寄存器命令之前，必须先设置写使能锁存器（WEL）位
 */
static void EnableNorflashWrite(void) {
    NOR_FLASH_CS_SELECT();
    SPI_RW_FUNCTION(NORFLASH_WRITE_ENABLE);
    NOR_FLASH_CS_DESELECT();
}



/**
 * @desc  : 等待闪存写完成
 * @date  : 2025-04-24
 * @author: AnRui
 * @note  : 查看状态寄存器的状态来判断
 *         状态寄存器是16bit 其中第0位是WIP
 *         当WIP位设置为1时，表示设备在编程/擦除/写入状态寄存器进度中忙碌；
 *         当WIP位设置为0时，表示设备不在编程/擦除/写入状态寄存器进度中忙碌。
 *         命令 NORFLASH_READ_STATUS_REGISTER_1 代表读前8位
 *         命令 NORFLASH_READ_STATUS_REGISTER_2 代表读后8位
 */
static void WaitNorflashWriteEnd(void) {
    uint32_t timeout = OUT_TIME_TIMES;
    NOR_FLASH_CS_SELECT();
    SPI_RW_FUNCTION(NORFLASH_READ_STATUS_REGISTER_1);

    while ((SPI_RW_FUNCTION(DUMMY_BYTE) & 0x01) && timeout) {
        timeout--;
        TIME_SPACE_FUNCTION();
    }
    NOR_FLASH_CS_DESELECT();
}




/**
 * @desc  : 根据地址擦除扇区
 * @date  : 2025-04-25
 * @author: AnRui
 * @param : eraseAddr - {参数说明}:
 * @note  : 扇区只能整个擦除 地址属于哪个扇区就删除那个扇区
 *          GD25Q16在执行扇区擦除时，硬件会自动将输入地址向下对齐到最近的扇区起始边界
 *          虽然硬件会自动对齐，但建议软件层仍应保证地址按扇区大小对齐
 *          0 1 2 3 4 5 6 7    4    9%4=4  4-4=0
 */
static void EraseNorflashSector(uint32_t eraseAddr) {
    // 强制进行扇区地址对齐
    // NORFLASH_SECTOR_SIZE - 1 = 0xFFF (二进制: 0000 1111 1111 1111)
    // ~(0xFFF) = 0xFFFFF000 (二进制: 1111 1111 1111 0000 0000 0000)
    // 核心就是 地址减去扇区大小
    eraseAddr = eraseAddr & ~(NORFLASH_SECTOR_SIZE - 1);
    EnableNorflashWrite();
    NOR_FLASH_CS_SELECT();
    SPI_RW_FUNCTION(NORFLASH_SECTOR_ERASE_4KB);
    SPI_RW_FUNCTION((eraseAddr & 0xFF0000) >> 16);
    SPI_RW_FUNCTION((eraseAddr & 0x00FF00) >> 8);
    SPI_RW_FUNCTION(eraseAddr & 0xFF);
    NOR_FLASH_CS_DESELECT();
    WaitNorflashWriteEnd();
}

/**
 * @desc  : 根据地址和长度擦除扇区
 * @date  : 2025-04-25
 * @author: AnRui
 * @param : eraseAddr - {参数说明}:
 * @param : len - {参数说明}:
 * @note  :
 */
static void EraseNorflashForWrite(uint32_t eraseAddr, uint32_t len) {
    // 检查
    if (eraseAddr > NORFLASH_MAX_ADDR || len == 0 || len > (NORFLASH_MAX_ADDR - eraseAddr + 1)) { return; }
    uint8_t sectorNum = 0;
    uint8_t addrOffset = eraseAddr % NORFLASH_SECTOR_SIZE; 	// mod运算求余在一扇区内的偏移，若eraseAddr是NORFLASH_SECTOR_SIZE整数倍，运算结果为0
    uint8_t remainSpace = NORFLASH_SECTOR_SIZE - addrOffset;

    if (len > remainSpace)      // 跨扇区
    {
        EraseNorflashSector(eraseAddr); // 擦本扇区
        eraseAddr += remainSpace;       // 对齐到扇区地址
        len -= remainSpace;
        sectorNum = len / NORFLASH_SECTOR_SIZE;
        sectorNum += (len % NORFLASH_SECTOR_SIZE == 0 ? 0 : 1);
        while (sectorNum) {
            EraseNorflashSector(eraseAddr);
            eraseAddr += NORFLASH_SECTOR_SIZE;
            sectorNum--;
        }
    } else  // 没有跨扇区
    {
        EraseNorflashSector(eraseAddr);
    }
}

/**
 * @desc  : 向某一页写入数据
 * @date  : 2025-04-24
 * @author: AnRui
 * @param : addr - {参数说明}:
 * @param : pBuffer - {参数说明}:
 * @param : len - {参数说明}:
 * @note  : 写数据之前 需要先擦除扇区
 */
static void ProgramNorflashPage(uint32_t addr, uint8_t* pBuffer, uint32_t len) {
    EnableNorflashWrite(); // 
    NOR_FLASH_CS_SELECT();
    SPI_RW_FUNCTION(NORFLASH_PAGE_PROGRAM);
    SPI_RW_FUNCTION((addr & 0xFF0000) >> 16);
    SPI_RW_FUNCTION((addr & 0x00FF00) >> 8);
    SPI_RW_FUNCTION(addr & 0xFF);
    while (len--) {
        SPI_RW_FUNCTION(*pBuffer);
        pBuffer++;
    }
    NOR_FLASH_CS_DESELECT();
    WaitNorflashWriteEnd();
}



/**
 * @desc  : 向norflash写入数据
 * @date  : 2025-04-24
 * @author: AnRui
 * @param : writeAddr - {参数说明}:
 * @param : pBuffer - {参数说明}:
 * @param : len - {参数说明}:
 * @note  :
 */
void WriteNorflashData(uint32_t writeAddr, uint8_t* pBuffer, uint32_t len) {
    // 检测
    if (writeAddr > NORFLASH_MAX_ADDR || len == 0 || len > (NORFLASH_MAX_ADDR - writeAddr + 1)) { return; }
    // 擦除扇区
    EraseNorflashForWrite(writeAddr, len);

    uint8_t pageNum = 0;
    // mod运算求余在一页内的偏移，若writeAddr是NORFLASH_PAGE_SIZE整数倍，运算结果为0 
    // 一页是256 例: 300 % 256 = 44 
    uint8_t addrOffset = writeAddr % NORFLASH_PAGE_SIZE;
    // 该页剩余空间
    uint32_t remainSpace = NORFLASH_PAGE_SIZE - addrOffset;

    // 发送数据的长度与 该页剩余的bit数对比    
    if (len > remainSpace) {
        ProgramNorflashPage(writeAddr, pBuffer, remainSpace);  // 写满本页
        writeAddr += remainSpace;                              // 对齐到下一页地址
        pBuffer += remainSpace;                                // 写一个要发送的数据地址
        len -= remainSpace;                                    // 剩余要发送的数据个数
        pageNum = len / NORFLASH_PAGE_SIZE;                    // 还需要发送的页数
        pageNum += (len % NORFLASH_PAGE_SIZE == 0) ? 0 : 1;    // 需要还有剩余数据就再发送一页
        while (pageNum) {
            ProgramNorflashPage(writeAddr, pBuffer, NORFLASH_PAGE_SIZE);  // 写整页数据
            writeAddr += NORFLASH_PAGE_SIZE;
            pBuffer += NORFLASH_PAGE_SIZE;
            len -= NORFLASH_PAGE_SIZE;
            pageNum--;
        }
    } else {
        ProgramNorflashPage(writeAddr, pBuffer, len);
    }
}

/**
 * @desc  : norflash测试代码
 * @date  : 2025-04-25
 * @author: AnRui
 * @note  :
 */
void NorflashDrvTest(void) {
    uint8_t mid = 0;
    uint16_t did = 0;
    uint32_t BUFFER_SIZE = 100;
    uint32_t FLASH_ADDRESS = 0x000002;
    uint8_t  bufferWrite[BUFFER_SIZE];
    uint8_t  bufferRead[BUFFER_SIZE];

    printf("***************************\n");
    printf("********GD25Qxx test start********\n");
    printf("***************************\n");
    ReadNorflashID(&mid, &did);
    printf("Norflash MID: 0x%X, DID: 0x%X\n", mid, did);
    for (uint16_t i = 0; i < BUFFER_SIZE; i++) {
        bufferWrite[i] = i;
        // printf("0x%02X ", bufferWrite[i]);
    }
    printf("\n start write \n");
    WriteNorflashData(FLASH_ADDRESS, bufferWrite, BUFFER_SIZE);
    printf("\n start read...\n");
    ReadNorflashData(FLASH_ADDRESS, bufferRead, BUFFER_SIZE);

    for (uint16_t i = 0; i < BUFFER_SIZE; i++) {
        if (bufferRead[i] != bufferWrite[i]) {
            printf("0x%02X ", bufferRead[i]);
            printf("test error,please check!\n");
            return;
        }
        printf("0x%02X", bufferRead[i]);

    }
    printf("\n test success!\n");
}

